#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

using namespace std;

//下面我们创建一个线程来看看简单单的效果
//此时主线程和新线程都在执行,说明有两个执行流
//可以使用ps -aL来进行查看线程LWP的相关信息
//-a 显示现行终端机下的所有进程，包括其他用户的进程
//-L 显示线程LWP(lightweight process)轻量级进程的相关信息

// Linux OS调度的时候,看的是PID还是LWP?
//看的是LWP,同一个进程的多个PCB里面保存的是PID,代表的是属于同一个进程
//实际操作系统进行调度的时候看的是LWP

//如何理解我们之前单独一个进程的情况?
//也就是这个进程的PID == LWP
// void* thread_run(void* args)
// {
//     const char* id = (const char*)args;
//     while(1)
//     {
//         cout << "i am " << id << "thread" << "id is " << getpid() << endl;
//         sleep(1);
//     }
// }

// int main()
// {
//     pthread_t tid;
//     pthread_create(&tid, nullptr, thread_run, (void*)"thread 1");

//     while(1)
//     {
//         cout << "i am main thread, id is " << getpid() << endl;
//         sleep(1);
//     }
//     return 0;
// }

//线程的创建
// pthread_create函数
// 功能:
//     创建一个新的线程
// 原型:
//     int phtread_create(pthread_t* thread, const pthread_attr_t* attr,
//     void* (*start_routine)(void*), void* arg);
// 参数:
//     thread:返回创建的线程ID
//     attr:设置线程的属性,attr为nullptr表示使用默认属性
//     start_routine:是一个函数的地址,线程启动后要执行的函数
//     arg:传给线程启动函数的参数
// 返回值:
//     成功返回0,失败返回错误码
// PS:
//     1.传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以提示错误
//     2.pthread系列的函数出错是不会设置全局变量errno(而大部分其他POSIX{可移植操作系统接口}函数会这么做)
//     而是将错误码通过返回值进行返回
//     3.pthread系列函数同样也提供了线程内的errno变量,以支持其他使用errno的代码
//     对于pthread系列函数的错误,建议通过返回值进行判断
//     因为读取返回值比读取线程内的errno变量的开销更小

// pthread_self函数
// 功能:
//     返回调用的线程的线程ID
// 原型:
//     pthread_t pthread_self(void);
// PS:
//     1.phtread_create函数会产生一个线程ID,存放在第一个参数指向的地址中
//     该线程ID和我们之前说的线程ID不是同一回事
//     2.前面我们所讲的ID属于简称调度的范围,因为进程是轻量级进程,是操作系统调度器的最小单位,
//     所以需要一个数值来唯一表示该进程
//     3.pthread_create函数第一个参数指向一个虚拟内存单元,该内存单元的地址就是新创建的线程的线程ID
//     属于线程库的范围,线程库的后续操作,就是根据该线程ID来进行操作的
//     4.线程库体统了pthread_self函数,可以用来获取线程自身的ID
//     5.phtread_t到底是什么类型,取决于实现,对于Linux目前的线程库实现而言
//     pthread_t类型是线程ID,可以理解为一个长整型,就是一个进程地址空间上的一个地址

// void* thread_run(void* args)
// {
//     while(1)
//     {
//         // cout << "i am new thread, and my thread id is " << pthread_self() << endl;

//         //这里我们获取到的id是pthread库的线程id,不是Linux内核中的LWP
//         //pthread库(用户层的描述线程的属性和数据结构)的线程id是一个进程地址空间的虚拟地址
//         //这个地址里面保存的是关于这个线程的一些东西
//         //比如线程自己运行时的临时数据,比如这个线程自己的私有栈
//         //这是描述线程的用户级控制块
//         //这个用户级控制块里面肯定包含了LWP,通过LWP才能找到属于自己调度的线程
//         printf("i am new thread, and my thread id is 0X%x\n", pthread_self());
//         sleep(1);
//     }
//     return nullptr;
// }

// int main()
// {
//     pthread_t tid;

//     pthread_create(&tid, nullptr, thread_run, nullptr);

//     while(1)
//     {
//         // cout << "i am main thread, and my thread id is " << pthread_self() << endl;
//         printf("i am main thread, and my thread id is 0X%x\n", pthread_self());
//         sleep(1);
//     }
//     return 0;
// }

//线程的等待
//线程为什么需要等待?
//一般而言,线程也是需要被等待的,如果不等待,可能会导致类似于"僵尸进程"的问题
//这个僵尸进程能看到效果的就是主线程,主线程在ps中的状态会呈现为defunct,如果主线程退出,其他的线程还可以继续执行
//如果不是主线程退出,其他的线程退出,一般不会导致其他的线程在PS里面呈现defunct
//已经退出的线程,其空间没有被释放,仍在进程的地址空间内
//创建新的进程不会复用刚才退出线程的地址空间

// pthread_join函数
// 功能:
//     等待线程结束
// 原型:
//     int pthread_join(ptthread_t thread, void** value_ptr);
// 参数:
//     thread:线程ID
//     value_ptr:它指向一个指针,是指向线程的返回值,线程的返回值也是指针
// 返回值:
//     成功返回0,失败返回错误码
// PS:
//     1.如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值
//     2.如果thread线程被别的线程调用pthread_cancel异常终止掉
//     value_ptr所指向的单元里面存放的是常数PTHREAD_CANCELED
//     3.如果thread线程是自己调用pthread_exit终止的,value_ptr所指向单元存放的是pthread_exit的参数
//     4.如果对thread线程的终止状态不敢兴趣,可以传nullptr给value_ptr

// void *thread_run(void *args)
// {
//     cout << "i am " << (char *)args << "thread" << endl;
//     if (strcmp((const char *)args, "one") == 0)
//     {
//         int* p = new int;
//         *p = 233;
//         sleep(2);
//         //导致线程异常,因为线程是属于进程的一部分,所以最终导致整个进程异常
//         // int x = 10;
//         // x /= 0;
//         return (void*)p;
//     }
//     else
//     {
//         sleep(2);
//         const char* p = new char[20];
//         p = "315牛逼!";
//         return (void*)p;
//     }
// }

// int main()
// {
//     pthread_t tid_one;
//     pthread_t tid_two;
//     pthread_create(&tid_one, nullptr, thread_run, (void*)"one");
//     pthread_create(&tid_two, nullptr, thread_run, (void*)"two");

//     //指针变量,本身就可以充当某种容器保存数据
//     //对于线程的异常,不需要进行异常的返回接收,因为线程异常,会导致整个进程产生异常
//     //这里的返回值可以接收比如对象的地址(不能是临时的)
//     void* one_return_status = nullptr;
//     void* two_return_status = nullptr;
//     //pthread_join(tid_one, &one_return_status);
//     //pthread_join(tid_two, &two_return_status);

//     //printf("thread one return status:%d\n", (*(int*)one_return_status));
//     //printf("thread two return status:%s\n", (char*)two_return_status);

       //delete one_return_status;
       //delete two_return_status;
//     while (1)
//     {
//         cout << "i am main thread" << endl;
//         sleep(1);
//     }
//     return 0;
// }

//线程的终止
//如果要终止线程有三种办法:
// 1.从线程函数中进行return操作,这种方法对主线程不适用,从main函数return相等于调用exit
// 2.线程可以调用pthread_exit终止自己
// exit是终止进程,如果是终止一个线程的话,不要在其他线程中调用
// 3.一个线程可以调用pthread_cancel终止同一个进程中的另一个线程

// pthread_exit函数
// 功能:
//     终止调用这个函数的线程
// 原型:
//     void pthread_exit(void* value_ptr);
// 参数:
//     value_ptr:输出型参数,可以传出自己想要的信息,pthread_join函数可以进行获取,但是不能是局部变量
// 返回值:
//     没有返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自己)
// PS:
//     pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的
//     不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了

// pthread_cancel函数
// 功能:
//     取消一个正在执行的线程
// 原型:
//     int pthread_cancel(pthread_t thread);
// 参数:
//     thread:线程ID
// 返回值:
//     成功返回0,失败返回错误码

// void* thread1(void* arg)
// {
//     cout << "thread 1 exiting..." << endl;
//     int *p = new int;
//     *p = 2;
//     pthread_exit((void* )p);
// }

// void* thread2(void* arg)
// {
//     while(1)
//     {
//        cout << "thread 2 running..." << endl;
//        sleep(1);
//     }
//     return nullptr;
// }

// int main()
// {
//     //因为pthread1调用了pthread_join后,再创建的pthread2,所以它们的id是一样的
//     pthread_t tid1;
//     pthread_t tid2;
//     void* ret;

//     //thread exit
//     pthread_create(&tid1, nullptr, thread1, nullptr);
//     //pthread_join(tid1, &ret);
//     //printf("thread return, thread id 0X%x, retunr code %d\n", tid1, *(int* )ret);
//     //free(ret);

//     //thread 3 cancel by other
//     pthread_create(&tid2, nullptr, thread2, nullptr);
//     sleep(3);
//     pthread_cancel(tid2);
//     pthread_join(tid2, &ret);
//     //pthread.h里面PTHREAD_CANCELED如下
//     //#define PTHREAD_CANCELED ((void *) -1)
//     if (ret == PTHREAD_CANCELED)
//          printf("thread return, thread id 0X%x, retunrn code PTHREAD_CANCELED\n", tid2);
//     else 
//     {
//          printf("thread return, thread id 0X%x, retunrn nullptr\n", tid2);
//     }
    
//     return 0;
// }

//分离线程
//1.默认情况下,新创建的线程是joinable的,线程退出后
// 需要对其pthread_join操作,否则无法释放资源,从而造成系统泄露
// 2.如果不关系线程的返回值,join是一种负担,因为会阻塞该线程
// 这个时候,我们可以告诉操作系统,线程退出时,自动释放线程资源

// pthread_detach函数
// 功能:
//     将指定线程进行分离
// 原型:
//     int pthread_detach(pthread_t thread)
// 返回值:
//     成功返回0,失败返回错误码
// PS:
// 1.可以时线程组内其他线程对目标线程进行分离,也可以时线程自己分离(调用pthread_self)
// 2.joinable和分离是冲突的,一个线程不能及时joinable的又是分离的

void* thread_run(void* arg)
{
    printf("%s\n", (char* )arg);
    return nullptr;
}

int main()
{
    pthread_t tid;
    if (pthread_create(&tid, nullptr, thread_run, (void*)"thread1 running...") != 0)
    {
        perror("create thread error");
        return 1;
    }

    int ret = 0;
    pthread_detach(tid);//很重要,要让线程先分离,再等待

    if (pthread_join(tid, nullptr) == 0)
    {
        cout << "pthread wait success" << endl;
    }
    else 
    {
        cout << "pthread wait failed" << endl;
        ret = 1;
    }

    return ret;
}